﻿using System;
using System.IO;
using System.Net;
using System.Windows.Threading;

namespace Gamelight.Data
{
	public class ClientAccessPolicyMissingException : Exception
	{
		internal ClientAccessPolicyMissingException()
			: base("ClientAccessPolicy.xml file is missing from server.")
		{ }
	}

	public abstract class WebLoader
	{
		private const int DEFAULT_MAX_ATTEMPTS = 3;
		private const int DEFAULT_MILLISECOND_DELAY = 1000;

		private string url;
		private Uri uri;
		private int attempts = 0;
		private int maxAttempts = DEFAULT_MAX_ATTEMPTS;
		private int millisecondDelayBetweenAttempts = DEFAULT_MILLISECOND_DELAY;
		protected bool complete = false;

		public WebLoader(string url)
		{
			this.url = url;
			this.uri = new Uri(this.url, UriKind.Absolute);
		}

		public WebLoader(string url, int maxAttempts, int millisecondDelayBetweenAttempts)
			: this(url)
		{
			this.maxAttempts = maxAttempts;
			this.millisecondDelayBetweenAttempts = millisecondDelayBetweenAttempts;
		}

		public string URL
		{
			get { return this.url; }
		}

		public void TryFetch()
		{
			WebClient webClient = new WebClient();
			webClient.OpenReadCompleted += this.ReadCompleted;
			webClient.OpenReadAsync(this.uri);

			++this.attempts;
		}

		public bool IsComplete { get { return this.complete; } }

		private bool timedOut = false;

		public bool IsFailed { get { return this.timedOut; } }

		protected abstract void ApplyStreamResult(Stream stream);

		private void ReadCompleted(object sender, OpenReadCompletedEventArgs e)
		{
			WebClient webClient = (WebClient)sender;

			webClient.OpenReadCompleted -= this.ReadCompleted;

			if (e.Error == null)
			{
				Stream stream = e.Result;

				this.ApplyStreamResult(stream);
			}
			else if (e.Error is System.Security.SecurityException)
			{
				throw new ClientAccessPolicyMissingException();
			}
			else
			{
				if (this.attempts < this.maxAttempts)
				{
					DispatcherTimer timer = new DispatcherTimer();
					timer.Interval = new TimeSpan(0, 0, 0, 0, this.millisecondDelayBetweenAttempts);
					timer.Tick += this.RetryTimerHandler;
					timer.Start();
				}
			}
		}

		private void RetryTimerHandler(object sender, EventArgs e)
		{
			DispatcherTimer timer = (DispatcherTimer)sender;
			timer.Stop();
			timer.Tick -= this.RetryTimerHandler;
			this.TryFetch();
		}
	}
}